// i.d(t, { u: () => Monitor })
import aggregationTypeEnum from "../enum/aggregationType.enum"
import funcUtil from "../utils/func.util"
import { removeArrayItem } from "../utils/array.util"
import { phaseState } from "../enum/phase.enum"
import Observable from "../observer/observable"
import EngineContext from "../EngineContext"

export default class Monitor {
  target: Observable
  updatePhaseSync: EngineContext
  diffObject: any
  diffRecord: any
  originalData: any
  aggregationType: any
  changedHandlers: any[]
  nextFramePromise: Promise<any>
  constructor(e: Observable, config: any, updatePhaseSync: EngineContext) {
    this.updatePhaseSync = updatePhaseSync
    this.target = e
    this.clearDiffRecord()
    this.target.onChanged(() => {
      this.diffRecord = funcUtil.diffObject(this.originalData, funcUtil.deepCopy(this.target))
      this.handleAggregation()
    })
    this.aggregationType = config.aggregationType
    this.changedHandlers = []
  }
  hasDiffRecord() {
    return Object.keys(this.diffRecord).length > 0
  }
  getDiffRecord() {
    return this.diffRecord
  }
  clearDiffRecord() {
    this.originalData = funcUtil.deepCopy(this.target)
    this.diffRecord = {}
  }
  onChanged(e) {
    this.changedHandlers.push(e)
  }
  removeOnChanged(e) {
    removeArrayItem(this.changedHandlers, e)
  }
  commitChanges() {
    this.triggerChangeHandlers(this.diffRecord)
  }
  handleAggregation() {
    switch (this.aggregationType) {
      case aggregationTypeEnum.Immediate:
        this.triggerChangeHandlers(this.diffRecord)
        break
      case aggregationTypeEnum.NextFrame:
        if (!this.updatePhaseSync) throw new Error("handleAggregation() -> Next frame aggregation requested, but updatePhaseSync is null")
        if (!this.nextFramePromise) {
          this.nextFramePromise = this.updatePhaseSync
            .after(phaseState.Begin)
            .then(() => {
              this.triggerChangeHandlers(this.diffRecord)
            })
            .finally(() => {
              this.nextFramePromise = void 0
            })
        }

        break
      case aggregationTypeEnum.Duration:
        throw new Error("AggregationType.Duration: Please implement me :)")
      case aggregationTypeEnum.Manual:
    }
  }
  triggerChangeHandlers(e) {
    if (this.changedHandlers.length > 0) for (const t of this.changedHandlers) t(e)
  }
}
